package dan.backend
import chisel3._
import chisel3.util._
import dan.common._
import dan.common.Consts._
import dan.utility.ImplicitCast._

class FuncCtrlSigs extends CoreBundle {
    val br_type        = UInt(BR_BITS.W)
    val op_fcn         = UInt((new AluFuncCode).SZ_ALU_FN.W)
    val op1_sel        = UInt(OP1T_BITS.W)
    val op2_sel        = UInt(OP2T_BITS.W)
    val imm_sel        = UInt(IMMT_BITS.W)
    val rf_wen         = Bool()
    val csr_cmd        = UInt(CSR_BITS.W)

    def decode(uopc: UInt, table: Iterable[(BitPat, List[BitPat])]) = {
        val decoder = DecodeLogic(uopc, AluRRdDecode.default, table)
        val sigs = Seq(
            br_type, op_fcn, op1_sel, op2_sel, imm_sel, rf_wen, csr_cmd
        )
        sigs zip decoder foreach {case(s,d) => s := d}
        this
    }
}

trait RRdDecodeConstants {
    val aluFn = new AluFuncCode
    val mulFn = new MultFuncCode
    val divFn = new DivFuncCode
    val cntFn = new CntFuncCode
    val default: List[BitPat] =
        List[BitPat](BR_N, aluFn.FN_ADD, OP1T_X, OP2T_X, IMM_X, N, CSR_N)
    val table: Array[(BitPat, List[BitPat])]
}

object AluRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                      br type
                //                        |                    op1 sel     op2 sel
                //                        |                       |           |      immsel    csr_cmd
                //                        |                       |           |        | rf wen |
                //                        |       alu fcn         |           |        |   |    |
                //                        |          |            |           |        |   |    |
            BitPat(UOP_SRLIW)    -> List(BR_N  , aluFn.FN_SRL  , OP1T_RS, OP2T_IMM , IMM_U5 , Y, CSR_N),
            BitPat(UOP_SRAIW)    -> List(BR_N  , aluFn.FN_SRA  , OP1T_RS, OP2T_IMM , IMM_U5 , Y, CSR_N),
            BitPat(UOP_SRLW)     -> List(BR_N  , aluFn.FN_SRL  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_SRAW)     -> List(BR_N  , aluFn.FN_SRA  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_SLLIW)    -> List(BR_N  , aluFn.FN_SL   , OP1T_RS, OP2T_IMM , IMM_U5 , Y, CSR_N),
            BitPat(UOP_ORN)      -> List(BR_N  , aluFn.FN_ORN  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_ANDN)     -> List(BR_N  , aluFn.FN_ANDN , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_LU12IW)   -> List(BR_N  , aluFn.FN_ADD  , OP1T_ZERO,OP2T_IMM , IMM_U20, Y, CSR_N),
            BitPat(UOP_OR)       -> List(BR_N  , aluFn.FN_OR   , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_NOR)      -> List(BR_N  , aluFn.FN_NOR  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_ADDIW)    -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_IMM , IMM_S12, Y, CSR_N),
            BitPat(UOP_ANDI)     -> List(BR_N  , aluFn.FN_AND  , OP1T_RS, OP2T_IMM , IMM_U12, Y, CSR_N),
            BitPat(UOP_ORI)      -> List(BR_N  , aluFn.FN_OR   , OP1T_RS, OP2T_IMM , IMM_U12, Y, CSR_N),
            BitPat(UOP_XORI)     -> List(BR_N  , aluFn.FN_XOR  , OP1T_RS, OP2T_IMM , IMM_U12, Y, CSR_N),
            BitPat(UOP_SLTI)     -> List(BR_N  , aluFn.FN_SLT  , OP1T_RS, OP2T_IMM , IMM_S12, Y, CSR_N),
            BitPat(UOP_SLTUI)    -> List(BR_N  , aluFn.FN_SLTU , OP1T_RS, OP2T_IMM , IMM_S12, Y, CSR_N),
            BitPat(UOP_SLLW)     -> List(BR_N  , aluFn.FN_SL   , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_ADD)      -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_SUB)      -> List(BR_N  , aluFn.FN_SUB  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_SLT)      -> List(BR_N  , aluFn.FN_SLT  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_SLTU)     -> List(BR_N  , aluFn.FN_SLTU , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_AND)      -> List(BR_N  , aluFn.FN_AND  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_XOR)      -> List(BR_N  , aluFn.FN_XOR  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_BEQ)      -> List(BR_EQ , aluFn.FN_SUB  , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N),
            BitPat(UOP_BNE)      -> List(BR_NE , aluFn.FN_SUB  , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N),
            BitPat(UOP_BGE)      -> List(BR_GE , aluFn.FN_SLT  , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N),
            BitPat(UOP_BGEU)     -> List(BR_GEU, aluFn.FN_SLTU , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N),
            BitPat(UOP_BLT)      -> List(BR_LT , aluFn.FN_SLT  , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N),
            BitPat(UOP_BLTU)     -> List(BR_LTU, aluFn.FN_SLTU , OP1T_X  , OP2T_X   , IMM_S16, N, CSR_N)
        )
}

object JmpRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                      br type
                //                        |                    op1 sel     op2 sel
                //                        |                       |           |      IMM_sel  csr_cmd
                //                        |                       |           |        | rf wen |
                //                        |       alu fcn         |           |        |   |    |
                //                        |          |            |           |        |   |    |
            BitPat(UOP_JIRL)     -> List(BR_JR , aluFn.FN_ADD  , OP1T_PC , OP2T_NEXT, IMM_S16, Y, CSR_N),
            BitPat(UOP_BL)       -> List(BR_J  , aluFn.FN_ADD  , OP1T_PC , OP2T_NEXT, IMM_S26, Y, CSR_N),
            BitPat(UOP_PCADDU12I)-> List(BR_N  , aluFn.FN_ADD  , OP1T_PC , OP2T_IMM , IMM_U20, Y, CSR_N),
            BitPat(UOP_PCADDI)   -> List(BR_N  , aluFn.FN_ADD  , OP1T_PC , OP2T_IMM , IMM_S20, Y, CSR_N)
        )
}

object MulDivRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                      br type
                //                        |                    op1 sel     op2 sel
                //                        |                       |           |      IMM_sel    csr_cmd
                //                        |                       |           |        | rf wen |
                //                        |       alu fcn         |           |        |   |    |
                //                        |          |            |           |        |   |    |
            BitPat(UOP_MODWU)    -> List(BR_N  , divFn.FN_REMU , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_DIVWU)    -> List(BR_N  , divFn.FN_DIVU , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_DIVW)     -> List(BR_N  , divFn.FN_DIV  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_MODW)     -> List(BR_N  , divFn.FN_REM  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_MULHW)    -> List(BR_N  , mulFn.FN_MULH , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_MULHWU)   -> List(BR_N  , mulFn.FN_MULHU, OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N),
            BitPat(UOP_MULW)     -> List(BR_N  , mulFn.FN_MUL  , OP1T_RS, OP2T_RS , IMM_X  , Y, CSR_N)
        )
}

object MemRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                      br type
                //                        |                    op1 sel     op2 sel
                //                        |                       |           |     IMM_sel   csr_cmd
                //                        |                       |           |       |  rf wen |
                //                        |       alu fcn         |           |       |    |    |
                //                        |          |            |           |       |    |    |
            BitPat(UOP_LD)       -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_IMM , IMM_S12, N, CSR_N),
            BitPat(UOP_STA)      -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_IMM , IMM_S12, N, CSR_N),
            BitPat(UOP_STD)      -> List(BR_N  , aluFn.FN_X    , OP1T_RS, OP2T_RS , IMM_S12, N, CSR_N),
            BitPat(UOP_LLW)      -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_IMM , IMM_S14, N, CSR_N),
            BitPat(UOP_SC_AG)    -> List(BR_N  , aluFn.FN_ADD  , OP1T_RS, OP2T_ZERO, IMM_S14, N, CSR_N)
        )
}

object CsrRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                    br type
                //                      |                    op1 sel     op2 sel
                //                      |                       |           |      IMM_sel   csr_cmd
                //                      |                       |           |        |  rf wen |
                //                      |       alu fcn         |           |        |    |    |
                //                      |          |            |           |        |    |    |
            BitPat(UOP_CSRWR)   -> List(BR_N , aluFn.FN_ADD  , OP1T_RS , OP2T_ZERO, IMM_CSR, Y, CSR_W),
            BitPat(UOP_CSRRD)   -> List(BR_N , aluFn.FN_ADD  , OP1T_ZERO, OP2T_ZERO, IMM_CSR, Y, CSR_R),
            BitPat(UOP_CSRXCHG) -> List(BR_N , aluFn.FN_ADD  , OP1T_RS , OP2T_RS , IMM_CSR, Y, CSR_M),
            BitPat(UOP_ERET)    -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, CSR_E),
            BitPat(UOP_IDLE)    -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, CSR_I),
        )
}

object CntRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                    br type
                //                      |                    op1 sel     op2 sel
                //                      |                       |           |      IMM_sel     csr_cmd
                //                      |                       |           |        |  rf wen |
                //                      |       alu fcn         |           |        |    |    |
                //                      |          |            |           |        |    |    |
            BitPat(UOP_RDCNTIDW)-> List(BR_N , aluFn.FN_ADD  , OP1T_ZERO, OP2T_ZERO, IMM_CID, Y, CSR_R),
            BitPat(UOP_RDCNTVLW)-> List(BR_N , cntFn.FN_VL   , OP1T_X   , OP2T_X   , IMM_X  , Y, CSR_N),
            BitPat(UOP_RDCNTVHW)-> List(BR_N , cntFn.FN_VH   , OP1T_X   , OP2T_X   , IMM_X  , Y, CSR_N)
        )
}

object TLBRRdDecode extends RRdDecodeConstants {
    val table: Array[(BitPat, List[BitPat])] =
        Array[(BitPat, List[BitPat])](
                //                    br type
                //                      |                    op1 sel     op2 sel
                //                      |                       |           |      IMM_sel   csr_cmd
                //                      |                       |           |        |  rf wen |
                //                      |       alu fcn         |           |        |    |    |
                //                      |          |            |           |        |    |    |
            BitPat(UOP_TLBSRCH) -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, TLB_S),
            BitPat(UOP_TLBFILL) -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, TLB_F),
            BitPat(UOP_TLBRD)   -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, TLB_R),
            BitPat(UOP_TLBWR)   -> List(BR_N , aluFn.FN_ADD  , OP1T_X   , OP2T_X   , IMM_X  , N, TLB_W),
            BitPat(UOP_INVTLB)  -> List(BR_N , aluFn.FN_ADD  , OP1T_RS , OP2T_RS , IMM_X  , N, TLB_I),
        )
}

class RegisterReadDecode(supportedUnits: OpFuncType) extends CoreModule {
    val io = IO(new CoreBundle {
        val iss_valid = Input(Bool())
        val iss_uop   = Input(new UOp)

        val rrd_valid = Output(Bool())
        val rrd_uop   = Output(new UOp)
    })

    // Issued Instruction
    io.rrd_uop   := io.iss_uop

    var dec_table = AluRRdDecode.table
    if (supportedUnits.jmp)    dec_table ++= JmpRRdDecode.table
    if (supportedUnits.mem)    dec_table ++= MemRRdDecode.table
    if (supportedUnits.muldiv) dec_table ++= MulDivRRdDecode.table
    if (supportedUnits.csr) {
        dec_table ++= CsrRRdDecode.table
        dec_table ++= TLBRRdDecode.table
    }
    if (supportedUnits.cnt)    dec_table ++= CntRRdDecode.table
    val rrd_cs = Wire(new FuncCtrlSigs).decode(io.rrd_uop.uopType, dec_table)

    io.rrd_valid := io.iss_valid

    io.rrd_uop.ctrl.brType := rrd_cs.br_type
    io.rrd_uop.ctrl.oprand1Type := rrd_cs.op1_sel
    io.rrd_uop.ctrl.oprand2Type := rrd_cs.op2_sel
    io.rrd_uop.ctrl.immType := rrd_cs.imm_sel
    io.rrd_uop.ctrl.funcCode  := rrd_cs.op_fcn
    io.rrd_uop.ctrl.isLd := io.rrd_uop.uopType === UOP_LD  || io.rrd_uop.uopType === UOP_LLW
    io.rrd_uop.ctrl.isStA  := io.rrd_uop.uopType === UOP_STA || io.rrd_uop.uopType === UOP_SC_AG
    io.rrd_uop.ctrl.isStD  := io.rrd_uop.uopType === UOP_STD
    io.rrd_uop.ctrl.csrCmd := rrd_cs.csr_cmd
}



